%author: wxj233
%time: 2023.9.1 12:00

classdef SourceData < handle
    % 数据加载
    % 加载点云数据，包括按帧读取数据。
    % 完成数据的预处理，包括筛选和聚类。
    
    properties
        filepath  % 点云文件路径
        data  % 点云二维数组数据
        frame_start  % 每一帧的起始索引位置
        datalen  % data总数据长度
        index  % 记录当前帧索引,仅对getNextFrame()函数有效
    end

    methods
        function obj = SourceData(filepath)
            % 构造此类的实例
            % filepath: 点云数据文件路径
            obj.filepath = filepath;
            obj.loadFile();
            obj.index = 1;
        end
        
        function loadFile(obj)
            % 读取点云数据
            % 记录原始点云数据data.
            % 记录每一帧的开始位置frame_start.
            % 记录data数据的总长度datalen.
            obj.data = table2array(readtable(obj.filepath,'VariableNamingRule', 'preserve'));
            obj.datalen = size(obj.data, 1);  % 获取点云数据总行数
            obj.frame_start = [];  % 用于存储每一次出现0的位置

            for i = 1 : obj.datalen
                if obj.data(i, 2) == 0  % 第二列为ID
                    obj.frame_start = [obj.frame_start; i];
                end
            end

        end


        function readFile(obj)
            % 读取点云数据
            % 记录原始点云数据data.
            % 记录每一帧的开始位置frame_start.
            % 记录data数据的总长度datalen.
            fid= fopen(obj.filepath, "r+");
            fgetl(fid);  % 跳过首行
            while ~feof(fid)
                row = strsplit(fgetl(fid), ',');
                obj.data = [obj.data; str2double(row)];
            end
            fclose(fid);
            obj.datalen = size(obj.data, 1);  % 获取点云数据总行数
            obj.frame_start = [];  % 用于存储每一次出现0的位置

            for i = 1 : obj.datalen
                if obj.data(i, 2) == 0  % 第二列为ID
                    obj.frame_start = [obj.frame_start; i];
                end
            end

        end

        function frame = getFrame(obj, index)
            % 获取指定帧
            % index: 指定帧索引
            % return: frame: 返回指定帧数据，若没有值则返回空矩阵
            frame_start_len = size(obj.frame_start, 1);
            if index > frame_start_len
                frame = [];
                return 
            end

            if index ~= frame_start_len
                frame = obj.data(obj.frame_start(index):obj.frame_start(index+1)-1,:);
            else
                frame = obj.data(obj.frame_start(index):obj.datalen, :);
            end
        end

        function frame = getNextFrame(obj)
            % 获取下一帧如果没有下一帧则返回空矩阵
            % return frame: 返回下一帧数据
            frame = obj.getFrame(obj.index);
            obj.index = obj.index + 1;
        end

        function setIndex(obj, index)
            % 设置索引，仅对getNextFrame()函数有效
            % index: 要设置的索引值
            obj.index = index;
        end

        function [staticPoints, movingPoints, notNullP] = speedRcsFilter(~, frame, dyRcsMin, stRcsMin, y_left, y_right)  % ~表示忽略第一个参数的意思
            % 通过速度过滤点，分别返回静止的点集和运动的点集
            % frame: 单帧数据集
            % dyRcsMin: 动态目标聚类筛选的最小RCS
            % stRcsMin: 静态聚类目标最小RCS
            % V_max: 最大速度
            % y_left: 道路左边距
            % y_right: 道路右边距
            % return: staticPoints, movingPoints静止的点集和运动的点集
            staticPoints = frame((frame(:,5)==0)&(frame(:,6)>stRcsMin)&(frame(:,4)>y_left)&(frame(:,4)<y_right),:);  % 第五列为速度
            movingPoints = frame((abs(frame(:,5))>0.5)&(frame(:,5)<40)&(frame(:,6)>dyRcsMin)&(frame(:,4)>y_left)&(frame(:,4)<y_right),:);
            notNullP = [frame(1,1), 1, rand*-1, 0, 0, 0];
        end

        function [Eq_points, labels] = getEquivalentPoint(~, points, epsilon, minpts, zoomX, zoomY, zoomV)
            % 聚类，并且同一类的点，仅保存等效点。
            % points: 待聚类的点
            % epsilon: Epsilon neighborhood of a point.
            % minpts: Minimum number of neighbors required for core point.
            % zoomX: X轴缩放比例
            % zoomY: Y轴缩放比例
            % zoomV: 速度缩放比例
            % return: Eq_points 数据等效点
            labels = dbscan([zoomX*points(:,3), zoomY*points(:,4), zoomV*points(:,5)], epsilon, minpts,"Distance","cityblock");  % 对x、y和径向速度进行聚类
%             labels = dbscan([zoomX*points(:,3), zoomY*points(:,4), zoomV*points(:,5)], epsilon, minpts,"Distance","euclidean");  % 对x、y和径向速度进行聚类
%             [labels, ~] = DBSCAN([zoomX*points(:,3), zoomY*points(:,4), zoomV*points(:,5)], epsilon, minpts);
            labels_unique = unique(labels);
            Eq_points = [];
            for label = labels_unique'
                if label == -1
                    continue
                else
                    ids = (labels == label);
                    cls = points(ids,:);

                    [rcs, ~] = max(cls(:, 6));  % rcs取最大值比较有代表性
                    x = mean(cls(:, 3));
                    y = mean(cls(:, 4));
                    v = mean(cls(:, 5));
                    
                    x_diff = max(cls(:, 3)) - min(cls(:, 3));
                    y_diff = max(cls(:, 4)) - min(cls(:, 4));
                    
                    D = x_diff*y_diff;
                    P = [cls(1,1), cls(1,2), x, y, v, rcs, D, x_diff, y_diff];
                    Eq_points = [Eq_points; P];
                end
            end

            ids = (labels ~= -1);
            points(ids,:) = [];
            P = [points, zeros(size(points, 1), 3)];
            Eq_points = [Eq_points; P];
        end

    end
end

